<?php
/**
 * Created by PhpStorm.
 * User: tom_xia
 * Date: 2018/1/29
 * Time: 16:31
 */

namespace app\cls\tom;


use function Aws\filter;

class FFmpeg
{
    private $ffmpegPath = "";
    private $ffprobe = "";
    private $errorMsg = "";

    public function getLastError()
    {
        return $this->errorMsg;
    }

    public function __construct($is_GPU = false)
    {
        if($is_GPU){
            $this->ffmpegPath = "/app/build/bin/ffmpeg";
            $this->ffprobe = "/app/build/bin/ffprobe";
        }else{
            $this->ffmpegPath = "/usr/bin/ffmpeg";
            $this->ffprobe = "/app/ffmpeg/ffprobe";
            $this->video_black = "/usr/bin/video_black";
        }
    }
    public function formatFFmpegTime($duration)
    {
        $hour = intval($duration / 3600);
        $minute = intval(($duration-$hour*3600)/60);
        $second = intval(($duration-$hour*3600-$minute*60));
        return sprintf("%02d",$hour).":".sprintf("%02d",$minute).":".sprintf("%02d",$second);
    }

    //添加水印
    public function overlay($src,$desc,$overImage,$x,$y){
        $command = "{$this->ffmpegPath} -i '{$src}'  -i '{$overImage}' -acodec copy  -filter_complex 'overlay=x={$x}:y={$y}' {$desc} ";

        return exec($command);
    }
    //居中添加水印
    public function centerOverlay($src,$desc,$overImage){
        $x = "(main_w-overlay_w)/2";
        $y = "(main_h-overlay_h)/2";
        $path = dirname($desc);
        if(!file_exists($path)){
            mkdir($path,0744,true);
        }
        return $this->overlay($src,$desc,$overImage,$x,$y);
    }
    //截图
    public function getCoverFromVideo($video,$image,$time = 0){
        $dirName = dirname($image);
        if (!file_exists($dirName)) {
            exec("mkdir -m 777 -p {$dirName}");
        }
        $com = "{$this->ffmpegPath}  -i '{$video}' -y -f  image2  -ss {$time} -vframes 1  {$image}";
        ob_start();
        passthru($com, $status);
        ob_clean();
        if(file_exists($image)){
            return true;
        }else{
            $this->errorMsg = "截图失败";
            return false;
        }
    }

    /**
     * 裁剪视频
     * @param $oriVideoPath string 原视频路径
     * @param $videoTempPath string 临时存放文件路径
     * @params $cutParams array 裁剪时间段
     * @return array | bool 裁剪视频的路径
     */
    public function cutVideo($oriVideoPath,$videoTempPath,array $cutParams){
//        if(!file_exists($oriVideoPath)){
//            return false;
//        }
        if(!file_exists($videoTempPath)){
            mkdir($videoTempPath,0777,true);
        }
        $cutVideo = [];
        $oriFileName = pathinfo($oriVideoPath,PATHINFO_FILENAME);
        $fileExtension = pathinfo($oriVideoPath,PATHINFO_EXTENSION);
        if(function_exists("posix_getpid")){
            $suffix = posix_getpid();
            $suffix = "_p_".$suffix."_".time()."_";
        }else{
            $suffix = "_r_".mt_rand(10000,99999)."_".time()."_";
        }
        foreach($cutParams as $key=>$value){
            $startTime = $value["start"];
            $duration = $value["duration"];
            $cutVideoFileName = "{$oriFileName}{$suffix}{$key}.{$fileExtension}";
            $outVideoPath = rtrim($videoTempPath,"/")."/".$cutVideoFileName;
            $command = "{$this->ffmpegPath} -y  -ss $startTime -t $duration -i '{$oriVideoPath}' -vcodec copy  -acodec copy  {$outVideoPath}";
//            return $command;exit;
            exec($command);
            if(file_exists($outVideoPath)){
                $cutVideo[] = $outVideoPath;
            }else{
                $this->errorMsg = "【cutVideo】裁剪失败";
                return false;
            }
        }
        return $cutVideo;
    }

    /**
     * 合并视频
     * @param array $videos 要合并的视频文件
     * @param string $outVideo 合并完成后存储的视频文件
     * @param bool $isDelOriVideo 合并完成后是否删除原始视频
     * @return bool
     */
    public function mergeTVideo(array $videos,$outVideo,$isDelOriVideo = true){
        if(empty($videos)){
            return false;
        }
        foreach($videos as $video){
            if(!file_exists($video)){
                return false;
            }
        }
        $contactTsStr = implode("|",$videos);
//        $command = "{$this->ffmpegPath} -y -i 'concat:{$contactTsStr}' -c:a copy -c:v copy {$outVideo}";

        //其他视频合并命令 ffmpeg -f  concat -i C:\Users\69119\Desktop\shiping.txt -c copy C:\Users\69119\Desktop\合并优酷.flv
        $command = "{$this->ffmpegPath} -y -i 'concat:{$contactTsStr}' -c copy {$outVideo}";
        exec($command);
        if($isDelOriVideo){
            foreach ($videos as $video){
                unlink($video);
            }
        }
        if(file_exists($outVideo)){
            return true;
        }else{
            return false;
        }
    }

    /**
     * 把视频转成hls视频
     * @param $video
     * @return boolean
     */
    public function genHLSVideo($video,$outM3u8File,$tsFormat="o-%6d.ts")
    {
//        if(!file_exists($video)){
//            return false;
//        }
        //修改ffmpeg进程执行目录
        chdir(dirname($outM3u8File));
        $command = "{$this->ffmpegPath} -y -i '{$video}'  -vcodec h264_nvenc -profile:v baseline -preset slow -pix_fmt yuv420p -b:v 1.5M -r 25 -bufsize 1M -keyint_min 50 -g 50  -ac 2 -ab 128k -vf scale=\"trunc(oh*a/2)*2:720\" -bsf:v h264_mp4toannexb -hls_time 10 -hls_list_size 0 -hls_segment_filename {$tsFormat} {$outM3u8File}";

        exec($command);
        if(file_exists($outM3u8File)){
            return true;
        }else{
            return false;
        }
    }

    /**
     * 获取视频长度
     * @param $video
     * @return int
     */
    public function getVideoLen($video)
    {
        ob_start();
        passthru("{$this->ffprobe} -v quiet -print_format json -show_format '$video' 2>&1");
        $content = ob_get_clean();
        $arr = json_decode($content,true);
        return  isset($arr["format"]["duration"]) ?$arr["format"]["duration"] :0 ;
    }

    /**
     * 检查ts是否正确
     * @param $tsFile
     * @return bool
     */
    public function checkTs($tsFile)
    {
        $path = dirname($tsFile);
        exec("{$this->ffmpegPath} -i " . $tsFile . "  2> " . $path . "/tsinfo.txt ");
        $audionum = exec("cat " . $path . "/tsinfo.txt | grep 'Stream #0:' | grep 'Audio:' | wc -l");
        if ($audionum == 0) {
            return false;
        }
        $audionum = exec("cat " . $path . "/tsinfo.txt | grep 'Stream #0:' | grep 'Audio:' | grep '0 channels' | wc -l");
        if ($audionum > 0) {
            return false;
        }
        return true;
    }

    /**
     * 流转换为m3u8文件
     * @param $stream
     * @param $m3u8File
     * @param int $hls_time
     */
    public function streamToM3u8($stream,$m3u8File,$hls_time = 10)
    {
        $path = dirname($m3u8File);
        if(!is_dir($path)){
            mkdir($path,0777,true);
        }

        $command = "{$this->ffmpegPath} -re -i " . $stream . "  -c copy -b 1000k  -bsf:v h264_mp4toannexb -hls_time $hls_time -use_localtime 1 -hls_segment_filename '" . $path . "/v-%s.ts' " . $m3u8File . "2>/dev/null";
        exec($command);
    }

    /**
     * 音视频转换
     * @param $src string 源
     * @param $desc string  目的
     * @return bool
     */
    public function audioTrans($src,$desc)
    {
        $path = dirname($desc);
        if(!is_dir($path)){
            mkdir($path,0777,true);
        }
        $command = "{$this->ffmpegPath} -y  -i '{$src}' -acodec aac -ab 128k -ar 16k -ac 2 {$desc}";
        exec($command);
        if(file_exists($desc)){
            return true;
        }else{
            $this->errorMsg = "音频转换失败";
            return false;
        }
    }

    /**
     * 音视频混编
     * @param $audioPaths array 音频地址
     * @param $videoPath string 视频地址
     * @param $outputPath string 输出的地址
     * @param $isDelOriAudio boolean 是否删除原始视频音频
     * @return bool
     */
    public function mixAudioVideo(array $audioPaths,$videoPath,$outputPath,$isDelOriAudio = true)
    {
        if(!$this->resourceExist($videoPath)){
            $this->errorMsg = "【mergeAudioVideo】 视频文件{$videoPath}不存在";
            return false;
        }
        $outputDir = dirname($outputPath);
        if(!file_exists($outputDir)){
            mkdir($outputDir,0777,true);
        }
        //1.去除原始音频
//        if($isDelOriAudio){
//            $fileName = pathinfo($videoPath,PATHINFO_BASENAME);
//            $uuid = Tools::UUID();
//            $newVideoPath = $outputDir."/AN_{$uuid}_{$fileName}";
//            if(!$this->delAudio($videoPath,$newVideoPath)){
//                return false;
//            }
//            $videoPath = $newVideoPath;
//        }
        //2.合成音频
        if(function_exists("posix_getpid")){
            $suffix = posix_getpid();
            $accPath = "p_".$suffix."_".time().".aac";
        }else{
            $accPath = "r_".mt_rand(10000,99999)."_".time().".aac";
        }
        $audioInputData = $outputDir."/{$accPath}";
        if(!$this->mergeAudio($audioPaths,$audioInputData)){
            return false;
        }
        $isShortest = "";
        $videoLength = $this->getVideoLen($videoPath);
        $audioLength = $this->getVideoLen($audioInputData);
//        if($videoLength < $audioLength){
//            $isShortest = "-shortest";
//        }
        //3.合成音视频
//        $command = "{$this->ffmpegPath} -y -i '{$videoPath}' '{$audioInputData}' -filter_complex amix=inputs={$audioNum}:duration=first:dropout_transition={$audioNum} -vcodec h264_nvenc -acodec acc -r 25 -b:v 1.5M -keyint_min 50 -g 50  -ac 2 -ab 128k '{$outputPath}'";
        $command = "{$this->ffmpegPath} -y -i '{$videoPath}' -i '{$audioInputData}'  -map 0:v:0 -map 1:a:0  -c:v copy -c:a aac   -ac 2 -ar 44100  -ab 128k -movflags faststart {$isShortest} '{$outputPath}'";
        exec($command);
        @unlink($audioInputData);
        if(file_exists($outputPath)){
            return true;
        }else{
            $this->errorMsg = "【mergeAudioVideo】输出文件失败";
            return false;
        }
    }

    /**
     * 删除视频中的原始音频
     * @param $oriVideoPath
     * @param $outputPath
     * @return bool
     */
    public function delAudio($oriVideoPath,$outputPath)
    {
        $path = dirname($outputPath);
        if(!file_exists($path)){
            mkdir($path,0777,true);
        }
        if(!$this->resourceExist($oriVideoPath)){
            $this->errorMsg = "【delAudio】 文件不存在";
            return false;
        }
        $command = "{$this->ffmpegPath} -y -i '{$oriVideoPath}' -vcodec copy -an '{$outputPath}'";
        echo $command."\n";
        exec($command);
        if(!file_exists($outputPath)){
            $this->errorMsg = "【delAudio】输出文件失败";
            return false;
        }
        return true;
    }

    /**
     * 修改原始音频声音
     * @param $audio
     * @param $outputPath
     * @param int $multiple 放大倍数 整数 可以为负数
     * @return bool
     */
    public function modifyVolume($audio,$outputPath,$multiple=20)
    {
        $path = dirname($outputPath);
        if(!file_exists($path)){
            mkdir($path,0777,true);
        }
        if(!$this->resourceExist($audio)){
            $this->errorMsg = "【modifyVolume】 文件不存在";
            return false;
        }
        $command = "{$this->ffmpegPath} -y -i '{$audio}' -af \"volume={$multiple}dB\"  '{$outputPath}'";
        echo $command."\n";
        exec($command);
        if(!file_exists($outputPath)){
            $this->errorMsg = "【modifyVolume】输出文件失败";
            return false;
        }
        return true;
    }


    /**
     * 合并多个音频
     * @param array $audioPaths  音频地址
     * @param string $outputPath  输出地址
     * @return boolean
     */
    public function mergeAudio(array $audioPaths,$outputPath)
    {
        $path = dirname($outputPath);
        if(!file_exists($path)){
            mkdir($path,0777,true);
        }
        $audioInputData = "";
        foreach($audioPaths as $audioPath) {
            if (!$this->resourceExist($audioPath)) {
                $this->errorMsg = "【mergeAudio】 音频文件{$audioPath}不存在";
                return false;
            }
            $audioInputData .= " -i '{$audioPath}'";
        }
        $audioNum = count($audioPaths);
        $command = "{$this->ffmpegPath}  -y $audioInputData  -filter_complex amix=inputs={$audioNum}:duration=first:dropout_transition={$audioNum}  -vol 500 -ac 2 -ar 44100  -ab 128k {$outputPath}";
        exec($command);
        if(file_exists($outputPath)){
            return true;
        }else{
            $this->errorMsg = "音频合成失败";
            return false;
        }
    }

    /**
     * 检查资源是否存在
     * @param $resourcePath string 资源路径
     * @return bool
     */
    private function resourceExist($resourcePath){
        if(filter_var($resourcePath,FILTER_VALIDATE_URL)){
//            if(!get_headers($resourcePath)){
//                return false;
//            }
            return true;
        }else{
            if(!file_exists($resourcePath)){
                return false;
            }
        }
        return true;
    }

    /**
     * 转码
     * @param $noAudioOriVideoPath
     * @param $noAudioOutPutPath
     * @return bool
     */
    public function transCode($noAudioOriVideoPath, $noAudioOutPutPath,$lastPath="")
    {
        $path = dirname($noAudioOutPutPath);
        if(!file_exists($path)){
            mkdir($path,0777,true);
        }
        $command = "{$this->ffmpegPath} -y  -i '{$noAudioOriVideoPath}'   -c:v h264 -c:a aac -r 25 -b:v 1.5M -keyint_min 50 -g 50  -ac 2 -ar 44100  -ab 128k -movflags faststart '{$noAudioOutPutPath}'";
//        return $command;
        exec($command);
        if(file_exists($noAudioOutPutPath)){
//            $path1 = dirname($lastPath);
//            if(!file_exists($path1)){
//                mkdir($path1,0777,true);
//            }
//            $cmd="{$this->video_black} {$noAudioOutPutPath} {$lastPath}";
////            return $cmd;
//            exec($cmd);
//            if(file_exists($lastPath) && filesize($lastPath)>200){
//                return true;
//            }else{
//                $this->errorMsg = "视频转码为16:9失败";
//                return false;
//            }
            return true;
        }else{
            $this->errorMsg = "视频转码失败";
            return false;
        }
    }

    //视频转9:16
    public function trnas_video($VideoPath, $OutPutPath)
    {
        $path = dirname($VideoPath);
        if(!file_exists($path)){
            mkdir($path,0777,true);
        }
        $cmd="{$this->video_black} {$VideoPath} {$OutPutPath}";
//            return $cmd;
        exec($cmd);
        if(file_exists($OutPutPath) && filesize($OutPutPath)>200){
            return true;
        }else{
            $this->errorMsg = "视频转码为16:9失败";
            return false;
        }
    }

    /**
     * 获取视频流详细信息
     * @param $video
     * @return int
     */
    public function getVideoDetail($video,$type=1)
    {
        ob_start();
        if($type==1){
            passthru("{$this->ffprobe} -v quiet -print_format json -show_streams  '$video' 2>&1");
            $content = ob_get_clean();
            $arr = json_decode($content,true);
            $data=[];
            if(!empty($arr['streams'])){
                foreach($arr['streams'] as $k=>$v){
                    if($v['codec_type']=="video"){
                        $data['width']=$v['width'];
                        $data['height']=$v['height'];
                    }
                }
                return $data;
            }else{
                return $data;
            }
        }else if($type==2){
            passthru("{$this->ffprobe} -v quiet -print_format json -show_streams  '$video' 2>&1");
            $content = ob_get_clean();
            $arr = json_decode($content,true);
            return $arr;
        }else if($type==3){
            passthru("{$this->ffprobe} -v quiet -print_format json -show_format  '$video' 2>&1");
            $content = ob_get_clean();
            $arr = json_decode($content,true);
            return $arr;
        }else if($type==4){
            passthru("{$this->ffprobe} -v quiet -print_format json -show_streams '$video' 2>&1");
            $content = ob_get_clean();
            $arr = json_decode($content,true);
            $data=[];
            if(!empty($arr['streams'])){
                foreach($arr['streams'] as $k=>$v){
                    if($v['codec_type']=="video"){
                        $data['width']=$v['width'];
                        $data['height']=$v['height'];
                        $data['duration']=$v['duration'];
                    }
                }
                return $data;
            }else{
                return $data;
            }
        }

    }

    //截图
    public function getCoverFromVideo1($video,$image,$time = 0){
        $dirName = dirname($image);
        if (!file_exists($dirName)) {
            exec("mkdir -m 777 -p {$dirName}");
        }
        $com = "{$this->ffmpegPath}  -i '{$video}' -y -f  image2  -ss {$time} -vframes 1  {$image}";
//        return $com;
        ob_start();
        passthru($com, $status);
        $logData = ob_get_clean();
        file_put_contents("/data0/logs/host/get_cover.log",$logData."--".$_SERVER['SERVER_NAME']."--".$_SERVER['SERVER_ADDR']."\n",FILE_APPEND);
        if(file_exists($image)){
            return true;
        }else{
            $this->errorMsg = "截图失败";
            return false;
        }
    }

    /**
     * 转码1
     * @param $noAudioOriVideoPath//////视频路径
     * @param $noAudioOutPutPath//////转码后存储路径
     * @return bool
     */
    public function transCode1($noAudioOriVideoPath, $noAudioOutPutPath,$arr)
    {
        $path = dirname($noAudioOutPutPath);
        if(!file_exists($path)){
            mkdir($path,0777,true);
        }
//        $command = "{$this->ffmpegPath} -y  -i '{$noAudioOriVideoPath}'   -c:v h264 -c:a aac -r 25 -vf scale='trunc(oh*a/2)*2:min(480\,ih)' -b:v 600k   -ac 2 -ar 44100  -ab 64k '{$noAudioOutPutPath}'";
//        $command = "{$this->ffmpegPath} -y  -i '{$noAudioOriVideoPath}'   -c:v h264 -c:a aac -r 25 -vf scale='trunc(oh*a/2)*2:min(480\,ih)' -b:v 600k   -ac 2 -ar 44100  -ab 64k -movflags faststart '{$noAudioOutPutPath}'";
//        $command = "{$this->ffmpegPath} -y  -i '{$noAudioOriVideoPath}' -c:v h264 -c:a aac -r 25 -vf scale='270:480,pad=270:480:{$arr['add_width']}:{$arr['add_height']}:black' -b:v 600k   -ac 2 -ar 44100  -ab 64k -movflags faststart '{$noAudioOutPutPath}'";
        $command = "{$this->ffmpegPath} -y  -i '{$noAudioOriVideoPath}' -c:v h264 -c:a aac -r 25 -vf scale='720:1280,pad=720:1280:0:0:black' -b:v 1M   -ac 2 -ar 44100  -ab 64k -movflags faststart '{$noAudioOutPutPath}'";
//        return $command;
        exec($command);
        if(file_exists($noAudioOutPutPath)){
            return true;
        }else{
            $this->errorMsg = "视频转码失败";
            return false;
        }
    }

    /**
     * 旋转视屏90度
     */
    public function xuanzhaun($video_url,$out_video_url,$xuanzhuan=90){
        $commond="{$this->ffmpegPath} -i '{$video_url}' -metadata:s:v rotate='{$xuanzhuan}' -codec copy '{$out_video_url}'";
        exec($commond);
        if(file_exists($out_video_url)){
            unlink($video_url);
            return true;
        }else{
            $this->errorMsg = "视频旋转失败";
            return false;
        }
    }
}
